package com.jerry.usbserial.util

import android.app.Activity
import android.content.Context
import android.content.SharedPreferences
import android.os.Environment
import android.text.InputFilter
import android.text.SpannableStringBuilder
import android.text.Spanned
import android.view.View
import android.view.inputmethod.InputMethodManager
import android.widget.Toast
import androidx.core.text.isDigitsOnly
import androidx.fragment.app.Fragment
import com.google.gson.Gson
import org.greenrobot.eventbus.EventBus
import java.io.Closeable
import java.io.File
import java.io.FileInputStream
import java.io.InputStream
import java.lang.reflect.ParameterizedType
import java.lang.reflect.Type
import java.util.*


/**
 * Created by dugang on 2020/6/7.
 */

data class MenuItem(
    var id: Int,
    var name: String,
    var temputer: Int,
    var time: Int,
    var update: Boolean = false
)

data class CommitResult(
    val code: String,
    val msg: String,
    val add1: String?,
    val add1_crc: String?,
    val add2: String?,
    val add2_crc: String?,
    val add3: String?,
    val add3_crc: String?,
    val ver: String
)

enum class MsgEventCode { STATE, MESSAGE, PROCESS, COMPLETED, ERROR }

data class MsgEvent(val code: MsgEventCode, val msg: String? = null, val process: Int = 0)

fun MsgEvent.post() {
    EventBus.getDefault().post(this)
}

fun postState(msg: String) {
    MsgEvent(MsgEventCode.STATE, msg).post()

}

fun postMsg(msg: String) {
    MsgEvent(MsgEventCode.MESSAGE, msg).post()
}

fun postError(msg: String) {
    MsgEvent(MsgEventCode.ERROR, msg).post()
}

fun postProcess(process: Int) {
    MsgEvent(MsgEventCode.PROCESS, process = process).post()
}

fun postCompleted(msg: String) {
    MsgEvent(MsgEventCode.COMPLETED, msg).post()
}

//fun writeIO(`is`: InputStream, os: OutputStream) {
//    try {
//        val byteArray = ByteArray(4096)
//        while (true) {
//            val read = `is`.read(byteArray)
//            if (read == -1) break
//            os.write(byteArray, 0, read)
//        }
//    } finally {
//        closeIO(`is`, os)
//    }
//}

fun readIO(`is`: InputStream): ByteArray {
    try {
        val byteArray = ByteArray(`is`.available())
        `is`.read(byteArray)
        return byteArray
    } finally {
        closeIO(`is`)
    }
}

fun File.matchCrc(crc: String): Boolean {
    val bytes = readIO(FileInputStream(this))
    val fileCrc = CRCUtil.getCrc32(bytes).toHex().toUpperCase(Locale.getDefault())
    return fileCrc == crc
}

fun <T : Closeable> closeIO(vararg ios: T?) {
    ios.forEach { it?.close() }
}

fun ByteArray.toHex(): String {
    val result = StringBuilder()
    forEach {
        result.append(Character.forDigit((it.toInt() shr 4) and 0xF, 16))
        result.append(Character.forDigit(it.toInt() and 0xF, 16))
    }
    return result.toString()
}

fun ByteArray.copyInto(length: Int, def: Int = 0): ByteArray = copyInto(ByteArray(length) { def.toByte() })


fun String.hexToByteArray(): ByteArray {
    val data = ByteArray(length / 2)
    for (i in 0 until length step 2)
        data[i / 2] = ((Character.digit(this[i], 16) shl 4) + Character.digit(this[i + 1], 16)).toByte()
    return data
}

fun String.hexTo64ByteArray(): ByteArray = this.hexToByteArray().copyInto(64, 0)

fun MutableList<Byte>.to64ByteArray(): ByteArray = this.toByteArray().copyInto(64, 0)

fun <T : Comparable<T>> Context.setSp(key: String, value: T) {
    getSp().edit().apply {
        when (value) {
            is Boolean -> putBoolean(key, value as Boolean)
            is Int -> putInt(key, value as Int)
            is String -> putString(key, value as String)
            is Float -> putFloat(key, value as Float)
            is Long -> putLong(key, value as Long)
        }
    }.apply()
}

fun Context.getSp(): SharedPreferences = getSharedPreferences(applicationInfo.packageName, Context.MODE_PRIVATE)

fun Context.downloadPath(): String = getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS)!!.path

fun String.getFileName(): String {
    val strArray = split("/")
    return strArray[strArray.size - 1]
}


val Context.density: Float
    get() = resources.displayMetrics.density

val Context.scaledDensity: Float
    get() = resources.displayMetrics.scaledDensity


fun Context.dp2px(value: Float): Float = value * density + 0.5f

fun Context.sp2px(value: Float): Float = value * scaledDensity + 0.5f

fun Context.showToast(msg: String) {
    Toast.makeText(this, msg, Toast.LENGTH_SHORT).show()
}

fun Int.toIP(): String {
    return "${this and 0xff}.${this shr 8 and 0xff}.${this shr 16 and 0xff}.${this shr 24 and 0xff}"
}

fun Int.to4HByteArray(): ByteArray {
    val byteArray = ByteArray(4)
    byteArray[0] = (this shr 24).toByte()
    byteArray[1] = (this shr 16).toByte()
    byteArray[2] = (this shr 8).toByte()
    byteArray[3] = (this shr 0).toByte()
    return byteArray
}

fun Int.to4LByteArray(): ByteArray {
    val byteArray = ByteArray(4)
    byteArray[3] = (this shr 24).toByte()
    byteArray[2] = (this shr 16).toByte()
    byteArray[1] = (this shr 8).toByte()
    byteArray[0] = (this shr 0).toByte()
    return byteArray
}

fun Int.to2LByteArray(): ByteArray {
    val byteArray = ByteArray(2)
    byteArray[1] = (this shr 8).toByte()
    byteArray[0] = (this shr 0).toByte()
    return byteArray
}

fun Int.to2HByteArray(): ByteArray {
    val byteArray = ByteArray(2)
    byteArray[0] = (this shr 8).toByte()
    byteArray[1] = (this shr 0).toByte()
    return byteArray
}

fun Long.to4LByteArray(): ByteArray {
    val byteArray = ByteArray(4)
    byteArray[3] = (this shr 24).toByte()
    byteArray[2] = (this shr 16).toByte()
    byteArray[1] = (this shr 8).toByte()
    byteArray[0] = (this shr 0).toByte()
    return byteArray
}


class InputFilterMinMax(private val min: Int, private val max: Int) : InputFilter {

    init {
        if (min >= max) throw Exception("min value is greater than or equal to max value!")
    }

    override fun filter(source: CharSequence?, start: Int, end: Int, dest: Spanned?, dstart: Int, dend: Int): CharSequence {
        return if (source.toString().isDigitsOnly()) {
            val input = (dest.toString() + source.toString()).toInt()
            if (input in min..max) SpannableStringBuilder(source, start, end) else ""
        } else ""
    }
}


fun View.hideSoftInput() {
    clearFocus()
    (context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager).hideSoftInputFromWindow(windowToken, 0)
}

fun Activity.hideSoftInput() {
    (currentFocus ?: window.decorView).hideSoftInput()
}

fun Fragment.hideSoftInput() {
    activity?.hideSoftInput()
}

fun Any.toJson(): String = Gson().toJson(this)

inline fun <reified T : Any> String.jsonToList(): MutableList<T> =
    Gson().fromJson(this, object : ParameterizedType {
        override fun getRawType(): Type = MutableList::class.java

        override fun getOwnerType(): Type? = null

        override fun getActualTypeArguments(): Array<Type> = arrayOf(T::class.java)
    })

